package trading_strategy.strategies;

import org.apache.log4j.Logger;
import trading_strategy.Strategy;
import trading_strategy.events.AbstractMarketEvent;
import trading_strategy.events.EventManager;
import trading_strategy.events.filtered.AMMFilter;
import trading_strategy.events.filtered.Candle;
import trading_strategy.events.filtered.CandleFilter;
import trading_strategy.instruments.ListedInstrument;
import trading_strategy.orders.Order;
import trading_strategy.orders.SingleOrderAutomaton;
import trading_strategy.positions.InstrumentPosition;
import trading_strategy.positions.Trade;

import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TreeMap;

/**
 * Created by IntelliJ IDEA.
 * User: gderoujoux
 * Date: 24 mai 2010
 * Time: 16:26:16
 * To change this template use File | Settings | File Templates.
 */
public class PairStrategy extends Strategy implements SingleOrderAutomaton.OrderProvider, TriggerPositionEnterer.PositionEntererListener {
    public static boolean INTERACTIVE = false;
    private AMMFilter amm28;
    private int candleDurationInSeconds = 10;

    public static final Logger LOGGER = Logger.getLogger(PairStrategy.class);
    SingleOrderAutomaton automaton;
    private TriggerPositionExiter positionExiter;
    private TriggerPositionEnterer positionEnterer;
    private int eventCounter = 0;
    private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    private SimpleDateFormat totSdf = new SimpleDateFormat("yyyMMdd-HH:mm:ss");
    private Date date = this.date;

    private InstrumentPosition position;
    private static final char CSV_SEPARATOR = ';';

    enum State {
        PASSIVE, SIGNAL_DETECTED, IN_POSITION
    }

    ListedInstrument instrument1, instrument2;
    TreeMap<Long, Candle> candles1 = new TreeMap<Long, Candle>();
    TreeMap<Long, Candle> candles2 = new TreeMap<Long, Candle>();

    @Override
    public double getPnL() {
        return position.getPnL();
    }

    public double getTransactionCost() {
        return position.getTransactioCost();
    }

    AMMFilter.AMMEvent lastAMMEvent;
    State state = State.PASSIVE;

    Writer writer;

    public PairStrategy(Date date, ListedInstrument[] instruments, Writer writer) {
        this.date = date;
        this.instrument1 = instruments[0];
        this.instrument2 = instruments[1];
        this.automaton = new SingleOrderAutomaton(instrument1, this);
        this.position = new InstrumentPosition(instrument1);
        this.writer = writer;
    }

    @Override
    public void onMarketEvent(AbstractMarketEvent event) {
        boolean shouldCheckCandleSignal = false;
        if (event instanceof CandleFilter.CandleEvent) {
            eventCounter++;
            CandleFilter.CandleEvent ce = (CandleFilter.CandleEvent) event;
            Candle candle = ce.getCandle();

            long start = candle.getStart().getTime();
            if (ce.getInstrument().equals(instrument1)) {
                candles1.put(start, candle);
            } else if (ce.getInstrument().equals(instrument2)) {
                candles2.put(start, candle);
            }

            if (candles1.containsKey(start) && candles2.containsKey(start)) {
                Candle c1 = candles1.get(start);
                double vwap1 = c1.getVwap();
                Candle c2 = candles2.get(start);
                double vwap2 = c2.getVwap();
                double return1 = c1.getClose() / c1.getOpen() - 1d;
                double return2 = c2.getClose() / c2.getOpen() - 1d;
                try {
                    writer.write(totSdf.format(c1.getEnd()));
                    writer.write(',');
                    writer.write(String.valueOf(vwap1));
                    writer.write(',');
                    writer.write(String.valueOf(vwap2));
                    writer.write(',');
                    writer.write(String.valueOf(return1));
                    writer.write(',');
                    writer.write(String.valueOf(return2));
                    writer.write('\n');
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public boolean shouldStillSendOrder(Order order) {
        return true;
    }

    @Override
    public void onExecution(int qty, double price, Order order) {
        Trade trade = new Trade(order.getMatchingTimestamp(), date, qty, price, getClass().getName());
        position.addTrade(trade);
        if (state == State.SIGNAL_DETECTED) {
            if (positionEnterer != null) {
                positionEnterer.deactivated(order.getMatchingTimestamp());
                LOGGER.info("Position entered " + qty + "@" + price + " at " + new Date(order.getMatchingTimestamp() / 1000));
                positionEnterer = null;
                long mustExitBefore = order.getMatchingTimestamp() + 10 * candleDurationInSeconds * 1000000l;
                long mustConfirmBefore = order.getMatchingTimestamp() + candleDurationInSeconds * 1000000l;
                positionExiter = new TriggerPositionExiter(instrument1, this, automaton, trade, 3, 10, 1, mustExitBefore, mustConfirmBefore, amm28);
                state = State.IN_POSITION;
            }
        } else if (state == State.IN_POSITION) {
            if (positionExiter != null) {
                positionExiter.deactivated(order.getMatchingTimestamp());
                LOGGER.info("Position exited " + qty + "@" + price + " at " + new Date(order.getMatchingTimestamp() / 1000));
                positionExiter = null;
                state = State.PASSIVE;
            }
        }
    }

    public void register() {
        // Subscribe to events classes
        CandleFilter cf1 = new CandleFilter(instrument1, "candles", candleDurationInSeconds);
        cf1.subscribe();
        CandleFilter cf2 = new CandleFilter(instrument2, "candles2", candleDurationInSeconds);
        cf2.subscribe();
//        amm28 = new AMMFilter(instrument1, "AMM28", 20, cf1);
//        amm28.subscribe();

        // Make sure that we subscribe to AMM before candle, in order to get the AMM before the candle...
//        EventManager.getInstance().subscribe(getListener(), amm28);
        EventManager.getInstance().subscribe(getListener(), cf1);
        EventManager.getInstance().subscribe(getListener(), cf2);
    }

    public int getEventCounter() {
        return eventCounter;
    }

    @Override
    public void deactivated() {
        if (state == State.SIGNAL_DETECTED) {
            state = State.PASSIVE;
        }
    }

    @Override
    public void stop() {
        super.stop();
    }
}